package com.yeyks.common.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @program: admin_server
 * @description:
 * @author: chenzheng
 * @create: 2019-04-03 15:45
 **/
@Component("common_RedisUtil")
public class RedisUtil {

    //@Value("${token.expirationSeconds}")
    private int expirationSeconds;

    /*常量，各种实现方式都行，这里读取application.yml*/
    //@Value("${token.validTime}")
    private int validTime;

    //默认十秒
    private int defalutGetLockTimeOut = 10;
    //默认十秒
    private int defalutValueTimeOut = 10;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 查询key,支持模糊查询
     *
     * @param key 传过来时key的前后端已经加入了*，或者根据具体处理
     */
    public Set<String> keys(String key) {
        return redisTemplate.keys(key);
    }

    /**
     * 字符串获取值
     *
     * @param key
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 字符串存入值
     * 默认过期时间为2小时
     *
     * @param key
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value, 7200, TimeUnit.SECONDS);
    }

    /**
     * 字符串存入值
     *
     * @param expire 过期时间（毫秒计）
     * @param key
     */
    public void set(String key, String value, long expire) {
        redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
    }

    /**
     * 删出key
     * 这里跟下边deleteKey（）最底层实现都是一样的，应该可以通用
     *
     * @param key
     */
    public void delete(String key) {
        redisTemplate.opsForValue().getOperations().delete(key);
    }

    /**
     * 添加单个
     * 默认过期时间为两小时
     *
     * @param key    key
     * @param filed  filed
     * @param domain 对象
     */
    public void hset(String key, String filed, Object domain) {
        redisTemplate.opsForHash().put(key, filed, domain);
    }

    /**
     * 添加单个
     *
     * @param key    key
     * @param filed  filed
     * @param domain 对象
     * @param expire 过期时间（毫秒计）
     */
    public void hset(String key, String filed, Object domain, Integer expire) {
        redisTemplate.opsForHash().put(key, filed, domain);
        redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    /**
     * 添加HashMap
     *
     * @param key key
     * @param hm  要存入的hash表
     */
    public void hset(String key, HashMap<String, Object> hm) {
        redisTemplate.opsForHash().putAll(key, hm);
    }

    /**
     * 如果key存在就不覆盖
     *
     * @param key
     * @param filed
     * @param domain
     */
    public void hsetAbsent(String key, String filed, Object domain) {
        redisTemplate.opsForHash().putIfAbsent(key, filed, domain);
    }

    /**
     * 查询key和field所确定的值
     *
     * @param key   查询的key
     * @param field 查询的field
     * @return HV
     */
    public Object hget(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 查询该key下所有值
     *
     * @param key 查询的key
     * @return Map<HK ,  H V>
     */
    public Object hget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 删除key下所有值
     *
     * @param key 查询的key
     */
    public void deleteKey(String key) {
        redisTemplate.opsForHash().getOperations().delete(key);
    }

    /**
     * 判断key和field下是否有值
     *
     * @param key   判断的key
     * @param field 判断的field
     */
    public Boolean hasKey(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key,field);
    }

    /**
     * 判断key下是否有值
     *
     * @param key 判断的key
     */
    public Boolean hasKey(String key) {
        return redisTemplate.opsForHash().getOperations().hasKey(key);
    }

    /**
     * 判断此token是否在黑名单中
     *
     * @param token
     * @return
     */
    public Boolean isBlackList(String token){
        return hasKey("blacklist",token);
    }

    /**
     * 将token加入到redis黑名单中
     *
     * @param token
     */
    public void addBlackList(String token){
        hset("blacklist", token," true");
    }


    /**
     * 查询token下的刷新时间
     *
     * @param token 查询的key
     * @return HV
     */
    public Object getTokenValidTimeByToken(String token) {
        return redisTemplate.opsForHash().get(token, "tokenValidTime");
    }

    /**
     * 查询token下的刷新时间
     *
     * @param token 查询的key
     * @return HV
     */
    public Object getUsernameByToken(String token) {
        return redisTemplate.opsForHash().get(token, "username");
    }

    /**
     * 查询token下的刷新时间
     *
     * @param token 查询的key
     * @return HV
     */
    public Object getIPByToken(String token) {
        return redisTemplate.opsForHash().get(token, "ip");
    }

    /**
     * 查询token下的过期时间
     *
     * @param token 查询的key
     * @return HV
     */
    public Object getExpirationTimeByToken(String token) {
        return redisTemplate.opsForHash().get(token, "expirationTime");
    }

    public void setTokenRefresh(String token, String username, String ip){
        //刷新时间
        Integer expire = validTime* 24* 60* 60* 1000;

        hset(token, "tokenValidTime", DateUtils.getAddDayTime(validTime),expire);
        hset(token, "expirationTime",DateUtils.getAddDaySecond(expirationSeconds),expire);
        hset(token, "username",username,expire);
        hset(token, "ip",ip,expire);
    }

    //获取锁
    public boolean getLock(String key, int second, int secondTimeout) {
        secondTimeout *= 100;
        while (secondTimeout-- > 0) {
            try {
                ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
                Long time = System.currentTimeMillis() + (1000 * second);
                Boolean aBoolean = valueOperations.setIfAbsent(key, String.valueOf(time));
                if (aBoolean) {
                    return true;
                }
                String oldTime = valueOperations.get(key);
                if (null != oldTime && Long.valueOf(oldTime) < System.currentTimeMillis()) {
                    String oldTimeAfter = valueOperations.getAndSet(key, String.valueOf(time));
                    if (oldTime == oldTimeAfter) {
                        return true;
                    }
                }
            } catch (Exception e) {
            }
            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
        }
        return false;
    }

    //解锁
    public boolean delLock(String key) {
        try {
            redisTemplate.delete(key);
            return true;
        } catch (Exception e) {
        }
        return false;
    }

    //基于锁的同步操作方法
    public <T> T runWithLock(String key, int second, int secondTimeout, RedisLockDown<T> redisLockDown) {
        boolean lock = getLock(key, second, secondTimeout);
        if (lock) {
            try {
                T t = redisLockDown.down();
                return t;
            } finally {
                delLock(key);
            }
        }
        return null;
    }

    /**
     * 锁的操作方法
     *
     * @param key
     * @param redisLockDown
     * @param <T>
     * @return
     */
    public <T> T runWithLock(String key, RedisLockDown<T> redisLockDown) {
        return runWithLock(key,defalutGetLockTimeOut,defalutValueTimeOut,redisLockDown);
    }

    //同步接口
    public interface RedisLockDown<T> {
        T down();
    }

}

